#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include "common.h"
#include "ethserver.h"


extern int handlefd[MAXFD];




int tcpserver_init(void)
{
    int ethfd;              //套接字句柄   
    // 申请 socket
    ethfd = socket(AF_INET, SOCK_STREAM, 0);

    // 定义 sockaddr_in
    struct sockaddr_in skaddr;
    skaddr.sin_family = AF_INET;            // ipv4
    skaddr.sin_port   = htons(PORT);
    skaddr.sin_addr.s_addr = inet_addr(SERVER_IP);

    // bind，绑定 socket 和 sockaddr_in
    if (bind(ethfd,(struct sockaddr *)&skaddr,sizeof(skaddr)) == -1 ) {
        perror("bind error");
        return -1;
    }

    // listen监听端口号
    if (listen(ethfd, BACKLOG) == -1 ) {
        perror("listen error");
        return -1;
    }

    //返回套接字句柄，利用select机制，监测客户端的链接请求
    //调用accept建立链接，并生成新的文件描述符，用于读写操作
    return ethfd; 
}


/* Write "n" bytes to a descriptor. */
ssize_t eth_writen(const void *vptr, size_t n)
{
    size_t nleft;       //剩余要写的字节数
    ssize_t nwritten;   //已经写的字节数
    const char *ptr;    //write 的缓冲区
    ptr = vptr;         //把传参进来的 write 要写的缓冲区备份一份
    nleft = n;          //还剩余需要写的字节数初始化为总共需要写的字节数

    //检查传参进来的需要写的字节数的有效性
    while (nleft > 0) {
        if ( (nwritten = write(handlefd[CLIENTFD], ptr, nleft)) <= 0) { //把 ptr 写入 fd
            if (nwritten < 0 && errno == EINTR) //当 write 返回值小于 0 且因为是被信号打断
                nwritten = 0; /* and call write() again */
            else
                return(-1); /* error 其他小于 0 的情况为错误 */
        }
        nleft -= nwritten; //还剩余需要写的字节数=现在还剩余需要写的字节数-这次已经写的字节数
        ptr += nwritten; //下次开始写的缓冲区位置=缓冲区现在的位置右移已经写了的字节数大小
    }
    return(n); //返回已经写了的字节数
}


int udpserver_init(void)
{
    int ethfd;              //套接字句柄   

    // 申请 socket
    ethfd = socket(AF_INET, SOCK_DGRAM, 0);

    // 定义 sockaddr_in
    struct sockaddr_in skaddr;
    skaddr.sin_family = AF_INET;            // ipv4
    skaddr.sin_port   = htons(9998);
    skaddr.sin_addr.s_addr = inet_addr(SERVER_IP);  //绑定本地ip

    // bind，绑定 socket 和 sockaddr_in
    if (bind(ethfd,(struct sockaddr *)&skaddr,sizeof(skaddr)) == -1 ) {
        perror("bind error");
        return -1;
    }

    //返回套接字句柄，利用select机制，监测客户端的链接请求
    //调用accept建立链接，并生成新的文件描述符，用于读写操作
    return ethfd; 
}

/*
 *  udp发送
 *  ip: 192.168.1.ip    port:端口号
 */
void udp_send(uint8_t *buff, uint8_t len, uint8_t ip, uint16_t port)
{
    uint8_t nsend;
    uint8_t ip_str[20];
    struct sockaddr_in clentaddr;
    uint32_t skaddr_len = sizeof(clentaddr);

    //sprintf(ip_str, "192.168.1.%d", ip);

    clentaddr.sin_family = AF_INET;
    clentaddr.sin_port = htons(port);
    clentaddr.sin_addr.s_addr = inet_addr(ip_str);

    //printf("ip:%s, port:%d",ip_str,port);
    nsend = sendto(handlefd[UDPFD],buff, len,0,(struct sockaddr *) &clentaddr, skaddr_len);
    if(nsend < 0){
        printf("udp send error\n");
    }
    else if(nsend < len) {//还有数据没发完
        //发送剩余的字节数
        sendto(handlefd[UDPFD],&buff[nsend], (len - nsend), 0,(struct sockaddr *) &clentaddr, skaddr_len);
    }else{
        //发送完成
    }
}

uint32_t udp_recv(uint8_t *buff)
{
    uint32_t len;
    // 定义 sockaddr_in
    struct sockaddr_in clentaddr;
    uint32_t skaddr_len = sizeof(clentaddr);

    len = recvfrom(handlefd[UDPFD], buff, 100, 0,(struct sockaddr *) &clentaddr, &skaddr_len);
    return len;
}




